import math
import random
import pyglet
from vec2d import Vec2d
from pyglet.gl import *
import data
import pole
import particle
import window

import background

from ctypes import *

class Sprites:
    """All game sprites are declared, instantiated and managed from within this class. 
    Handles passing messages, drawing and updating all game sprites."""

    MIN_COUNT_START = 15 # minimum fish count target at start 
    MAX_SPAWN_RATE = 3 # maximum number to spawn per second
    MAX_COUNT = 15
    MIN_DECREASE = 0.1 # amount minimum fish decrease per second

    #List of all creatures in the game
    def __init__(self, game):
        """Function that initializes Sprites"""
        self.sprite_batch = pyglet.graphics.Batch()
        self.creatures = []
        self.game = game
        self.time = 0
        #Add a random number of creatures 
        self.add_random_creatures(self.MIN_COUNT_START / 3)
        self.next_spawn_time = 0
        self.creature_count_target = self.MIN_COUNT_START
        self.FISH_HOOKED = False

    def update(self, dt):
        """Function called by timer in pyglet, causes updates to sprites"""
        self.time += dt
        if (self.time > self.next_spawn_time and (
            len(self.creatures) < self.creature_count_target or random.random() > 1.0/60.0)):
            self.add_random_creatures(count=1)
            self.next_spawn_time = self.time + 1.0 / self.MAX_SPAWN_RATE
        self.creature_count_target -= self.MIN_DECREASE * dt
        for creature in self.creatures:
            #if creature is outside the game width remove it.
            if(((creature.x - creature.CREATURE_WIDTH) > self.game.width) or ((creature.x + creature.CREATURE_WIDTH) < 0)):
                #Stop the creature moving
                creature.bubble_emitter.kill()
                self.remove_creature(creature)
                self.add_random_creatures(count=1)
            else:
                creature.update(dt, self.game)
        self.draw()

    def draw(self):
        """Draw Sprites"""
        for creature in self.creatures:
            creature.draw(self.game)

    def add_random_creatures(self, count=1):
        """Skeleton to Add a random number of Creatures to each layers"""
        for i in range(count):
            if random.randint(1, 55000) == 1:
                # jackpot ding, ding, ding
                for i in range(20):
                    direction = random.choice((-1, 1))
                    start_position_x = (direction == -1) * 700 
                    fish = BigFish(self.game, start_position_x, random.randint(70, 300), 
                        sprites=self, direction=direction, velocity=random.randint(1,5))
                    self.add_creature(fish)

            if len(self.creatures) > self.MAX_COUNT:
                break # the sea is teeming!

            layers = [70, 140, 210, 290, 360]
            direction = random.choice((-1, 1))
            start_position_x = (direction == -1) * 700 

            roll = random.randint(1, 20)
            if roll < 3:
                fish = Eel(self.game, start_position_x, random.choice(layers), 
                    sprites=self, direction=direction, velocity=random.randint(1,4))
            elif roll == 3:
                fish = BigFish(self.game, start_position_x, random.choice(layers[:2]), 
                    sprites=self, direction=direction, velocity=random.randint(2,3))
            elif roll > 16:
                fish = MedFish(self.game, start_position_x, random.choice(layers[:4]), 
                    sprites=self, direction=direction, velocity=random.randint(2,5))         
            else:
                fish = SmallFish(self.game, start_position_x, random.choice(layers[2:]), 
                    sprites=self, direction=direction, velocity=random.randint(3,5))

            self.add_creature(fish)

    def add_creature(self, creature):
        """Add a creature to the list of creatures that get traveresed when update is called"""
        self.creatures.append(creature)
        self.game.collision_system.add(creature)

    def remove_creature(self, creature):
        """Delete a creature from the list of creatures that get traveresed when update is called"""
        self.creatures.remove(creature)
        if creature in self.game.collision_system:
            self.game.collision_system.remove(creature)


class Creature(object):
    """Parent/Base class of all sprites/objects/creatures. Contains values 
    common to all."""
    def __init__(self, x, y, image):
        #This base class needs a severe refactoring
        """Function that initializes creature"""
        #Image that the creature is based off of
        self.sprite = pyglet.sprite.Sprite(data.image(image))
        #X position of sprite
        self.sprite.x = x
        #Y position of sprite
        self.sprite.y = y
        #time
        self.TIME = 0
        #sprite = pyglet.sprite.Sprite(image)
        self.NAME = "Creature"
        #Number of points you get for eating the Creature
        self.POINTS = 0 
        #speed at which the creature is moving
        self.CREATURE_VELOCITY = 1
        #Creature weight
        self.CREATURE_MASS = 0
        #Might come in handy for jelly fish or determining how much damage explosives do
        self.CREATURE_DENSITY = 0
        #Creature speed
        self.CREATURE_HEIGHT = self.sprite.height 
        #Creature width
        self.CREATURE_WIDTH  = self.sprite.width
        #Amount of time creature lives
        self.CREATURE_LIFE = 0
        #Health
        self.CREATURE_HEALTH = 0

    def update(self, dt, game):
        """Function called by timer in pyglet updates status of creature"""
        self.TIME += dt
        self.sprite.x = self.sprite.x  +  self.CREATURE_VELOCITY
     
    def set_position(self, x, y):
       """Set the x,y,z position of creature"""
       self.sprite.x = x
       self.sprite.y = y

    def draw(self, game):
        """Draw creature"""
        self.sprite.draw()

    def resize(self, game):
        """Resize creature"""

    def get_x_value(self) :
            return self.sprite.x
    def die(self):
        """Creature is dead"""
        self.CREATURE_VELOCITY = 0

class Fish(object):
    """Fish creature. Now uses textured geometry."""

    SWIMMING_STATE = 0
    HOOKED_STATE = 1
    FLYING_STATE = 2
    LANDED_STATE = 3
    
    #Variable which shows area of fish than be hooked/interacted with
    collision_radius = 5
    #Correct the collision so it corrseponds with the creatures mouth
    collision_offset = Vec2d(5,9) 
    
    _alltextures = {}    
    
    def __init__(self, game, x, y, sprites, images, fin_image, segments = (0.2, 0.2, 0.2, 0.4), direction = 1, velocity = 1):

        """Function that initializes creature.
            segments: specify where the geometry will bend, as a list of floats which add up to 1.0 (what percent of the total each segment should be)
        """
        self.game = game
        #Image that the creature is based off of
        #self.image = data.image(image)
        #self.texture = self.image.get_texture()
        try:
            self.textures = self._alltextures[self.NAME]
        except KeyError:
            self.textures = [data.image(i).get_texture() for i in images]
            self._alltextures[self.NAME] = self.textures
        
        self.texture_index = 0
        self.texture = self.textures[self.texture_index]
        
        try:
            self.fin_texture = self._alltextures[fin_image]
        except KeyError:
            self.fin_texture = data.image(fin_image).get_texture()
            self._alltextures[fin_image] = self.fin_texture
        

        self.fin_angle = 90.0
        
        #For now, to avoid attribute errors
        self.sprite = sprites
        #time
        self.TIME = 0
        #Number of points you get for eating the Creature (defined in subclasses now)
        #self.POINTS = 0 
        #Direction of the fish
        self.direction = direction
        #speed at which the creature is moving
        self.CREATURE_VELOCITY = velocity
        #Creature weight
        self.CREATURE_MASS = 0
        #Might come in handy for jelly fish or determining how much damage explosives do
        self.CREATURE_DENSITY = 0
        #Creature speed
        self.CREATURE_HEIGHT = self.texture.height 
        #Creature width
        self.CREATURE_WIDTH  = self.texture.width
        
        self.CREATURE_ROTATION = 0.0
        
        #Amount of time creature lives
        self.life = 0
        
        #Health
        self.health = 0
        self.points = 0
        self.name = "Fish"
        
        self.scale = 1.0
        
        self.x = x
        self.y = y

        self.state = self.SWIMMING_STATE

        #Each segment has a corresponding angle.
        self.segments = [float(i) for i in segments]
        self.segment_angles = [0.0] * len(segments)
        
        t_coords = self.texture.tex_coords
        self.t_x = t_coords[3]
        self.t_y = t_coords[7]

        self.data = []
        self.tex = []
        
        self.vertex_list = pyglet.graphics.vertex_list( (len(segments)+1)*2, 'v3f','t2f',
             ('c3B', (255, 255, 255)*(len(segments)+1)*2),
        )
        
        fin_tex_coords = list(self.fin_texture.tex_coords)
        fin_t_x = fin_tex_coords[3]
        fin_t_y = fin_tex_coords[7]

        self.fin = pyglet.graphics.vertex_list( 4, 
            ('v3f', (0.0, 0.0, 0.0, self.fin_texture.width, 0.0, 0.0, self.fin_texture.width, self.fin_texture.height, 0.0, 0.0, self.fin_texture.height, 0.0)), 
            ('t3f', self.fin_texture.tex_coords), 
            ('c3B', (255, 255, 255)*4))
        
        self.calc_segments()
        self.bubble_emitter = particle.BubbleEmitter(
            self, speed=self.CREATURE_VELOCITY*self.direction*20,
            rate=5, sizes=[(30, 30), (20,20), (23,23), (17,17)])
        
    @property
    def collision_position(self):
        """Return x/y as instance of vec2d, expose as .attribute"""
        return Vec2d(self.x, self.y + self.CREATURE_HEIGHT / 2.0)            
 

    def on_collide(self, system, other):
        """Creature collided with something"""
        if isinstance(other, pole.FishingLine) and self.sprite.FISH_HOOKED == False: 
            # XXX We should probably have a separate hook class
            # since colliding with the line may mean something different
            self.sprite.FISH_HOOKED = True
            self.caught()
            system.remove(self)

    def caught(self):
        """Creature is caught"""
        if self.state == self.SWIMMING_STATE:
            self.state = self.HOOKED_STATE
            self.CREATURE_VELOCITY = 0
            self.bubble_emitter.kill()
            data.sound('whistle_ding.mp3').play()

    def calc_segments(self):
        """Generate the geometry for each segement."""
        #TODO: Re-use between fish!
        current_x = 0.0
        current_z = 0.0
        
        self.data = [current_x, 0.0, current_z, current_x, self.CREATURE_HEIGHT, current_z]
        
        self.tex = [self.t_x, 0.0, self.t_x, self.t_y]        
        
        segtotal = 0.0
        for i in range(len(self.segments)):
            theta = self.segment_angles[i]
            if i > 0:
                theta -= self.segment_angles[i-1]

            seg = self.segments[i]
            current_x = (self.CREATURE_WIDTH * seg) * math.cos(theta) + current_x
            current_z = (self.CREATURE_WIDTH * seg) * math.sin(theta) + current_z

            self.data += [current_x, 0.0, current_z, current_x, self.CREATURE_HEIGHT, current_z]

            segtotal += seg
            t_x = (self.t_x * (1.0 - segtotal))
            
            self.tex += [t_x, 0.0, t_x, self.t_y]

        self.vertex_list.vertices = self.data
        self.vertex_list.tex_coords = self.tex
        
    def draw(self, game):
        """Draw fish"""
        glPushMatrix()
        
        #Body position
        if self.direction > 0:
            glRotatef(180, 0.0, 1.0, 0.0)
            glTranslatef(-self.x, self.y, 0)            
        else:
            glTranslatef(self.x, self.y, 0)
            
        glScalef(self.scale, self.scale, self.scale)    
        glRotatef(self.CREATURE_ROTATION, 0.0, 0.0, 1.0)
        glBindTexture(self.texture.target, self.texture.id)
        glEnable(self.texture.target)
        self.vertex_list.draw(GL_QUAD_STRIP)
        glDisable(self.texture.target)

        #Fin position and rotation
        z_offset = (self.data[8] + self.data[14])/2.0
        glTranslatef(self.CREATURE_WIDTH * (self.segments[0] + self.segments[0]/2.0), self.CREATURE_HEIGHT / 4.0, z_offset)
        glRotatef(math.degrees( self.segment_angles[0]) + self.fin_angle, 0.0, 1.0, 0.0)

        glBindTexture(self.fin_texture.target, self.fin_texture.id)
        glEnable(self.fin_texture.target)   
        self.fin.draw(GL_QUADS)
        glDisable(self.fin_texture.target)
        glPopMatrix()
        
        if self.state == self.LANDED_STATE:
            self.SCORE_LABEL.draw()
    
    
    def set_position(self, x, y):
       """Set the x,y,z position of creature"""
       self.x = x
       self.y = y

    def update(self, dt, game):
        """Function called by timer in pyglet updates status of creature"""
        self.TIME += dt        
        
        #Just swim
        if self.state == self.SWIMMING_STATE:
            if self.direction > 0:
                self.x += self.CREATURE_VELOCITY
            if self.direction < 0:
                self.x -= self.CREATURE_VELOCITY

            self.CREATURE_ROTATION = math.sin(self.TIME * self.CREATURE_VELOCITY)*2

        #Wriggle faster and follow the hook's y coordinates
        elif self.state == self.HOOKED_STATE:
            if self.CREATURE_VELOCITY < 10:
                self.CREATURE_VELOCITY += 1
                
            if self.CREATURE_ROTATION > -90:
                self.CREATURE_ROTATION -= 5

            #Change this to call a get sprite method  
            hook_sprite = game.pole.line.hook.hook_sprite
            self.y = hook_sprite.y - hook_sprite.height + 16
            self.x = hook_sprite.x + hook_sprite.width / 2.0 + (
                self.texture.height / 2 * self.direction) - 10

            #if self.y >= game.water_level - 10:
            if game.pole.line.state == game.pole.line.DANGLING_STATE:
                self.state = self.FLYING_STATE
                self.sprite.FISH_HOOKED = False
                self.TIME = 0
                self.up_vel = 90#180
                self.x0 = self.x
                self.left_vel = -(self.x+3) / 2.0

        #Projectile motion towards boat
        elif self.state == self.FLYING_STATE:
            self.y = game.water_level + (self.up_vel *self.TIME - 50*self.TIME*self.TIME)
            self.x += self.left_vel * dt
            self.CREATURE_ROTATION = self.collision_position.get_angle() - 90
            
            if self.scale > 0.71:
                self.scale -= 0.01
            
            if self.TIME > 1.5 and self.y < game.water_level + 40:
                self.state = self.LANDED_STATE
                self.SCORE_LABEL.x = self.x
                self.SCORE_LABEL.y = self.y
                game.update_score(self.POINTS)
                
        #Display the score of the caught fish
        elif self.state == self.LANDED_STATE:
            if self.scale > 0.1:
                self.scale -= 0.1
                
            # if self.SCORE_LABEL.font_size < 36:
            #                self.SCORE_LABEL.font_size += 1
            self.SCORE_LABEL.y = self.SCORE_LABEL.y + 1
            
            if self.scale < 0.1 and self.SCORE_LABEL.y > game.height:
                #Remove this sprite from the game
                self.sprite.remove_creature(self)
                #self.SCORE_LABEL.y = 0
            
            
        #Swimming animation    
        self.fin_angle = math.sin(self.TIME * 5 * self.CREATURE_VELOCITY) * 20                      
        for i in range(len(self.segment_angles)):
            if i%2:
                self.segment_angles[i] = math.sin(self.TIME * 2 * self.CREATURE_VELOCITY) * 0.40
            else:
                self.segment_angles[i] = math.cos(self.TIME * 2 * self.CREATURE_VELOCITY) * 0.40
        self.bubble_emitter.update(dt)
        self.calc_segments()
        
class SmallFish(Fish):
    POINTS = 1
    SCORE_LABEL = pyglet.text.Label('+%d'%POINTS, color=(0, 200, 0, 255), font_size=16)
    
    def __init__(self, game, x, y, sprites, direction = 1, velocity = 1):
        self.NAME = 'SmallFish'        
        Fish.__init__(self, game, x, y, sprites, ["fish1.png", "fish2.png", "fish3.png"], "fin1.png", segments= (0.2, 0.2, 0.2, 0.4), direction=direction, velocity=velocity)

    def update(self, dt, game):
        Fish.update(self, dt, game)

        #Look freaked out when hooked
        if self.state == self.HOOKED_STATE:
            self.texture_index = 2
        #Otherwise, cycle animation
        elif self.TIME % 2 < 0.2:
            self.texture_index = 1
        else:
            self.texture_index = 0
        self.texture = self.textures[self.texture_index]

        
class MedFish(Fish):
    POINTS = 3
    SCORE_LABEL = pyglet.text.Label('+%d'%POINTS, color=(0, 200, 0, 255), font_size=16)
        
    def __init__(self, game, x, y, sprites, direction = 1, velocity = 1):
        self.NAME = 'MediumFish'
        Fish.__init__(self, game, x, y, sprites, ["med_fish1.png"], "med_fish_fin.png", segments= (0.2, 0.2, 0.2, 0.2, 0.2), direction=direction, velocity=velocity)

class BigFish(Fish):
    POINTS = 10
    SCORE_LABEL = pyglet.text.Label('+%d'%POINTS, color=(0, 200, 0, 255), font_size=16)    
    
    def __init__(self, game, x, y, sprites, direction = 1, velocity = 1):
        self.NAME = 'BigFish'        
        Fish.__init__(self, game, x, y, sprites, ["big_fish1.png"], "big_fish_fin.png", segments= (0.2, 0.2, 0.2, 0.2, 0.2), direction=direction, velocity=velocity)


class Eel(Fish):
    collision_radius = 30
    def __init__(self, game, x, y, sprites, direction = 1, velocity = 1):
        self.NAME = 'Eel'        
        Fish.__init__(self, game, x, y, sprites, ["eel.png", "eel2.png", "eel3.png", "eel4.png"], "eel_fin.png", segments = (0.2,)*5, direction=direction, velocity=velocity)    
        self.next_shock = self.TIME
        
    def update(self, dt, game):
        Fish.update(self, dt, game)
        if self.TIME % 2 < 0.2:
            self.texture_index = (self.texture_index + 1)%len(self.textures)
            self.texture = self.textures[self.texture_index]

    @property
    def collision_position(self):
        """Return x/y as instance of vec2d, expose as .attribute"""
        return Vec2d(self.x-18*self.direction, self.y + self.CREATURE_HEIGHT / 2.0)        

    def on_collide(self, system, other):
        if self.TIME > self.next_shock:
            #print 'Ouch!'
            self.game.time_color = (200, 200, 0, 255)
            self.game.time -= 30 # seconds removed from game timer
            self.next_shock = self.TIME + 3 # seconds between shocks
            data.sound('electrocute.mp3').play()
